package mx.kolobok.noteeditor;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.CDATA;
import org.jdom.Element;
import org.jdom.Text;
import org.jdom.filter.ContentFilter;
import org.jdom.filter.ElementFilter;
import org.jdom.filter.Filter;
import org.jdom.input.DOMBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import java.awt.*;
import java.io.*;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: Nikita Belenkiy
 * Date: 28.01.11
 * Time: 23:01
 */
public class NoteManager implements Observer {
    private static final Logger logger = Logger.getLogger(NoteManager.class);
    private static final String PATH_TO_NOTES = ("true".equals(System.getProperty("devmode")) ? "." : System.getProperty("user.home")) + File.separator + "notes";
    @NotNull
    private static final String FILE_NAME = PATH_TO_NOTES + File.separator + "defaultNotes.xml";
    @NotNull
    private static String dateFormatStr = "dd.MM.yyyy HH:mm:ss";
    @NotNull
    private static final DateFormat dateFormat = new SimpleDateFormat(dateFormatStr);
    @NonNls
    private static final String NOTE_ELEMENT_NAME = "note";
    @NotNull
    private List<Note> notes = new ArrayList<Note>();

    private NoteManager(InputStream is) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException, ParseException {
        loadNotes(is);
    }

    private void loadNotes(InputStream is) throws ParserConfigurationException, IOException, SAXException, XPathExpressionException, ParseException {
        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
        org.jdom.Document jdomDocument = new DOMBuilder().build(document);
        @SuppressWarnings({"unchecked"})
        List<Element> noteElements = jdomDocument.getRootElement().getContent(new ElementFilter(NOTE_ELEMENT_NAME));
        int length = noteElements.size();
        notes = new ArrayList<Note>(length);
        Filter filter = new ContentFilter(ContentFilter.CDATA);
        for (Element item : noteElements) {
            List content = item.getContent(filter);
            String text = content.size()==0?"":((Text) content.get(0)).getText();
            String title = item.getAttribute("title").getValue();
            String created = item.getAttribute("date-created").getValue();
            String updated = item.getAttribute("date-updated").getValue();
            Attribute colorAttribute = item.getAttribute("color");
            Color color  =colorAttribute==null?null: Color.decode(colorAttribute.getValue());
            createNote(title, text, dateFormat.parse(created), dateFormat.parse(updated), color);
        }
        logger.info("Notes count: " + notes.size());
    }

    public void saveNotes() throws NoteManagerException {
        logger.info("Saving notes to disk");
        Element rootElement = new Element("notebook");
        org.jdom.Document document = new org.jdom.Document(rootElement);
        for (Note note : notes) {
            Element element = new Element("note");
            element.setAttribute("title", note.getTitle());
            element.setAttribute("date-created", dateFormat.format(note.getCreated()));
            element.setAttribute("date-updated", dateFormat.format(note.getUpdated()));
            Color color = note.getColor();
            if (color != null) {
                element.setAttribute("color", Integer.toHexString(color.getRGB()));
            }
            element.addContent(new Text(" \n\n"));
            String text = note.getText();
            element.addContent(new CDATA(text == null ? "" :text));
            rootElement.addContent(element);
        }
        Format format = Format.getPrettyFormat();

        XMLOutputter outputter = new XMLOutputter(format);
        try {
            outputter.output(document, new FileOutputStream(FILE_NAME));
        } catch (IOException e) {
            throw new NoteManagerException(e);
        }
    }

    public List<Note> getNotes() {
        return Collections.unmodifiableList(notes);
    }

    public Note createNote(final String title, final String text) throws NoteManagerException {
        Date created = new Date();
        return createNote(title, text, created, created, null);
    }

    private Note createNote(String title, String text, Date created, Date updated, @Nullable Color color) {
        Note note = new Note(title, text, created, updated, color);
        note.addObserver(this);
        notes.add(note);
        return note;
    }

    public Note deleteNote(int noteNumber) throws NoteManagerException {
        Note remove = notes.remove(noteNumber);
        saveNotes();
        return remove;
    }

    @Override
    public void update(Observable o, Object arg) {
        try {
            saveNotes();
        } catch (NoteManagerException e) {
            e.printStackTrace();
        }
    }

    public int moveToPosition(Note note, int newIndex) throws NoteManagerException {
        int oldIndex = notes.indexOf(note);
        if (oldIndex != newIndex) {
            notes.add(newIndex, note);
            notes.remove(newIndex > oldIndex ? oldIndex : oldIndex + 1);
            saveNotes();
        }
        return oldIndex;
    }

    @NotNull
    public static NoteManager createNoteManager() {
        try {
            File file = new File(FILE_NAME);
            if (!file.exists()) {
                File notesDir = new File(PATH_TO_NOTES);
                if (!notesDir.exists()) {
                    notesDir.mkdirs();
                }
                FileOutputStream newFileOut = new FileOutputStream(file);
                InputStream templateInputStream = NoteManager.class.getClassLoader().getResourceAsStream("defaultNotes.template.xml");
                FileUtil.fromInToOut(templateInputStream, newFileOut);
            } else {

                FileUtil.backup(FILE_NAME);
            }
            InputStream resourceAsStream = new FileInputStream(FILE_NAME);
            return new NoteManager(resourceAsStream);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

}
